unit frAttach;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
  Dialogs, cxControls, cxContainer, cxEdit, cxImage, Menus, ShellApi,
  ImgList, uDAScriptingProvider, uDADataTable, uDAMemDataTable,fClientDataModule,
  uDARemoteDataAdapter,VirtualTrees;

type TOnNewAttachSelected = procedure (Sender: TObject; AFilename: string) of object;

type TAttachNodeData = record
  Filename: string;
  Desc: string;
  Size: Integer;
  Id: TGUID;
  IconId: Integer;
  Ext: string;
end;
type PAttachNodeData = ^TAttachNodeData;

type
  TfrmAttachs = class(TFrame)
    vstAttachs: TVirtualStringTree;
    popAttach: TPopupMenu;
    itemAdd: TMenuItem;
    itemDelete: TMenuItem;
    itemSaveAs: TMenuItem;
    odlgAttachs: TOpenDialog;
    ilFiles: TImageList;
    sdlgAttachs: TSaveDialog;
    tblAttach: TDAMemDataTable;
    rdaAttachs: TDARemoteDataAdapter;
    procedure vstAttachsDblClick(Sender: TObject);
    procedure vstAttachsChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
    procedure vstAttachsHeaderClick(Sender: TVTHeader; Column: TColumnIndex;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure vstAttachsGetImageIndex(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
      var Ghosted: Boolean; var ImageIndex: Integer);
    procedure itemAddClick(Sender: TObject);
    procedure vstAttachsGetNodeDataSize(Sender: TBaseVirtualTree;
      var NodeDataSize: Integer);
    procedure vstAttachsGetPopupMenu(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex; const P: TPoint;
      var AskParent: Boolean; var PopupMenu: TPopupMenu);
    procedure vstAttachsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
  private
    { Private declarations }
    FId: TGUID;
    FOnNewAttachSelected: TOnNewAttachSelected;
    FOnCountChange: TNotifyEvent;
    function FindFileIcon(AFilename: string): Integer;
  public
    { Public declarations }
    procedure Clear;
    procedure LoadAttachments(ListFiles: TStringList);
    procedure InsertAttachments;
    procedure Initialize(Id: TGUID);
    procedure Finalialize;
    property  OnNewAttachSelected: TOnNewAttachSelected read FOnNewAttachSelected write FOnNewAttachSelected;
    property OnCountChange: TNotifyEvent read FOnCountChange write FOnCountChange;
  end;

const TempPath = 'c:\temp\';
const AttachmentsPath = 'Attachments\';

implementation

uses
  tools, ActiveX, uDAInterfaces;

{$R *.dfm}

procedure TfrmAttachs.Initialize(Id: TGUID);
var
  node: PVirtualNode;
  nd: PAttachNodeData;
  filename: string;
  filestream: TFileStream;
begin
  vstAttachs.Align := alClient;
  FId := Id;
  tblAttach.Close;
  tblAttach.Where.Clear;
  tblAttach.Where.AddCondition('bpa_pid', cEqual, QuotedStr(GUIDToString(FId)));
  tblAttach.Open;
  if not tblAttach.IsEmpty then
  begin
    tblAttach.First;
    while not tblAttach.EOF do
    begin
      node := vstAttachs.AddChild(nil);
      nd := vstAttachs.GetNodeData(node);
      nd^.Filename := tblAttach.FieldByName('bpa_filename').AsString;
      nd^.Desc := tblAttach.FieldByName('bpa_desc').AsString;
      nd^.Size := tblAttach.FieldByName('bpa_size').AsInteger;
      nd^.Id := tblAttach.FieldByName('bpa_id').AsGuid;
      nd^.Ext := tblAttach.FieldByName('bpa_ext').AsString;
      filename := TempPath + GuidToString(nd^.Id) + nd^.Ext;
      filestream := TFileStream.Create(filename, fmCreate);
      nd^.IconId := FindFileIcon(filename);
      FreeAndNil(filestream);
      DeleteFile(filename);
      tblAttach.Next;
    end;
  end;
end;

procedure TfrmAttachs.Finalialize;
begin
  vstAttachs.Clear;
  ilFiles.Clear;
  tblAttach.Close;
end;


procedure TfrmAttachs.vstAttachsChange(Sender: TBaseVirtualTree;
  Node: PVirtualNode);
var
  nd: PAttachNodeData;
begin
  if Node = nil then Exit;
  if Sender.SelectedCount > 1 then Exit;
  if ssAlt in KeyboardStateToShiftState then Exit;

  nd := Sender.GetNodeData(Node);
  if Assigned(FOnNewAttachSelected) then
    FOnNewAttachSelected(self, nd^.Filename);
end;

function TfrmAttachs.FindFileIcon(AFilename: string):Integer;
var
  fileinfo: TSHFileinfo;
  flags: Integer;
  icon: Ticon; 
begin
  if not FileExists(AFilename) then
    flags := SHGFI_USEFILEATTRIBUTES or FILE_ATTRIBUTE_NORMAL
  else
    flags := 0;

  FillChar(fileinfo, SizeOf(fileinfo), #0);

  flags := flags or SHGFI_SYSICONINDEX or SHGFI_ICON;
  SHGetFileInfo(PChar(AFilename), 0 , fileinfo, SizeOf(TSHFileinfo), flags);
  
  icon := TIcon.Create;
  try
    icon.Handle := fileinfo.hIcon;
    Result := ilFiles.AddIcon(icon);
  finally
    icon.Handle := 0;
    DestroyIcon(fileinfo.hIcon);
    FreeAndNil(icon);
  end;  

end;

procedure TfrmAttachs.Clear;
begin
  vstAttachs.Clear;
  ilFiles.Clear;
end;

procedure TfrmAttachs.LoadAttachments(ListFiles: TStringList);
var
  i: Integer;
  filename: string;
  stream: TFileStream;
  node: PVirtualNode;
  nd: PAttachNodeData;
  guid: TGUID;
begin
  vstAttachs.BeginUpdate;
  try
    for i := 0 to ListFiles.Count - 1 do
    begin
      filename := ListFiles.Strings[i];
      if FileExists(filename) then
      begin
        stream := TFileStream.Create(filename, fmOpenRead or fmShareDenyNone);
        try
          node := vstAttachs.AddChild(nil);
          CoCreateGuid(guid);
          nd := vstAttachs.GetNodeData(node);
          nd^.Id := guid;
          nd^.Filename := ExtractFileName(filename);
          nd^.IconId :=FindFileIcon(filename);
          nd^.Size := stream.Size;
          nd^.Ext := ExtractFileExt(nd^.Filename);
        finally
          FreeAndNil(stream);
        end;
        tblAttach.Append;
        tblAttach.FieldByName('bpa_pid').AsGuid := FId;
        tblAttach.FieldByName('bpa_id').AsGuid := nd^.Id;
        tblAttach.FieldByName('bpa_filename').AsString := nd^.Filename;
        tblAttach.FieldByName('bpa_size').AsInteger := nd^.Size;
        tblAttach.FieldByName('bpa_desc').AsString := nd^.Desc;
        tblAttach.FieldByName('bpa_ext').AsString := nd^.Ext;
        tblAttach.FieldByName('bpa_file').LoadFromFile(filename);
        tblAttach.Post;        
      end;
    end;
  finally
    vstAttachs.EndUpdate;
    vstAttachs.SortTree(vstAttachs.Header.SortColumn,vstAttachs.Header.SortDirection);
  end;
end;

procedure TfrmAttachs.InsertAttachments;
var
  strlist: TStringList;
begin
  if odlgAttachs.Execute then
  begin
    strlist := TStringList.Create;
    strlist.Text := odlgAttachs.Files.Text;
    LoadAttachments(strList);
    FreeAndNil(strlist);
  end;

  if Assigned(FOnCountChange) then
    FOnCountChange(Self);
  

end;

procedure TfrmAttachs.itemAddClick(Sender: TObject);
begin
  InsertAttachments;
end;

procedure TfrmAttachs.vstAttachsDblClick(Sender: TObject);
var
  sql: string;
  nd: PAttachNodeData;
  filename: string;
begin
  if vstAttachs.FocusedNode = nil then
    Exit;

  nd := vstAttachs.GetNodeData(vstAttachs.FocusedNode);
  sql := Format('select bpa_file from b_productattachment where bpa_id=''%s''',[GUIDToString(nd^.Id)]);
  try
    OpenPublicQuery(sql);
    with ClientDataModule do
    begin
      filename := ExtractFilePath(Application.exename) + AttachmentsPath + GUIDToString(nd^.Id) + nd^.Ext;
      tblDynamic.FieldByName('bpa_file').SaveToFile(filename);
      ShellExecute(handle, 'open', PChar(filename),
            nil, nil, SW_SHOWNORMAL);      
    end;
  finally
    ClosePublicQuery;
  end;
end;

procedure TfrmAttachs.vstAttachsGetImageIndex(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  var Ghosted: Boolean; var ImageIndex: Integer);
var
  nd: PAttachNodeData;
begin
  nd := Sender.GetNodeData(Node);
  if (Column = 0) and (Kind in [ikNormal, ikSelected]) then
      ImageIndex := nd^.IconId;
end;

procedure TfrmAttachs.vstAttachsGetNodeDataSize(Sender: TBaseVirtualTree;
  var NodeDataSize: Integer);
begin
  NodeDataSize := SizeOf(TAttachNodeData);
end;

procedure TfrmAttachs.vstAttachsGetPopupMenu(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex; const P: TPoint;
  var AskParent: Boolean; var PopupMenu: TPopupMenu);
begin
  PopupMenu := popAttach;
end;

procedure TfrmAttachs.vstAttachsGetText(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  var CellText: WideString);
var
  nd: PAttachNodeData;
begin
  nd := Sender.GetNodeData(Node);
  case Column of
    0: CellText := nd^.Filename;
    1: CellText := SizeToString(nd^.Size);
    2: CellText := nd^.Desc;
  end;
end;

procedure TfrmAttachs.vstAttachsHeaderClick(Sender: TVTHeader;
  Column: TColumnIndex; Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
  if Button = mbLeft then begin
    //if same column then just reverse sorting
    if Sender.SortColumn = Column then begin
      if Sender.SortDirection = sdAscending then
        Sender.SortDirection := sdDescending
      else
        Sender.SortDirection := sdAscending;
    end
    else
      Sender.SortColumn := Column;
  end;
  vstAttachs.SortTree(vstAttachs.Header.SortColumn,vstAttachs.Header.SortDirection);  
end;

end.
